<?php

/**
 * Redis服务器:一个基于 Redis 协议的服务器程序，可以让我们使用 Redis 的客户端来连接这个服务。
 */

use Swoole\Redis\Server;

define('DB_FILE', __DIR__ . '/db');

$server = new Server("0.0.0.0", 9501, SWOOLE_BASE);

if (is_file(DB_FILE)) {
    $server->data = unserialize(file_get_contents(DB_FILE));
} else {
    $server->data = array();
}

//使用 setHandler() 方法来监听 Reids 命令，在这里我们看到了熟悉的 get、set 等命令的定义。
$server->setHandler('GET', function ($fd, $data) use ($server) {
    if (count($data) == 0) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"));
    }

    $key = $data[0];
    //指定了 $server->data ，可以将它看成是一个数据源，直接使用的就是一个文件，
    //直接在当前测试环境目录下创建一个叫做 db 的空文件就可以了。
    if (empty($server->data[$key])) {
        //使用 send() 方法来返回响应的命令信息，并通过 format() 方法格式化返回的响应数据。
        return $server->send($fd, Server::format(Server::NIL));
    } else {
        return $server->send($fd, Server::format(Server::STRING, $server->data[$key]));
    }
});

$server->setHandler('SET', function ($fd, $data) use ($server) {
    if (count($data) < 2) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'SET' command"));
    }

    $key = $data[0];
    $server->data[$key] = $data[1];
    return $server->send($fd, Server::format(Server::STATUS, "OK"));
});

$server->setHandler('sAdd', function ($fd, $data) use ($server) {
    if (count($data) < 2) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sAdd' command"));
    }

    $key = $data[0];
    if (!isset($server->data[$key])) {
        $array[$key] = array();
    }

    $count = 0;
    for ($i = 1; $i < count($data); $i++) {
        $value = $data[$i];
        if (!isset($server->data[$key][$value])) {
            $server->data[$key][$value] = 1;
            $count++;
        }
    }

    return $server->send($fd, Server::format(Server::INT, $count));
});

$server->setHandler('sMembers', function ($fd, $data) use ($server) {
    if (count($data) < 1) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'sMembers' command"));
    }
    $key = $data[0];
    if (!isset($server->data[$key])) {
        return $server->send($fd, Server::format(Server::NIL));
    }
    return $server->send($fd, Server::format(Server::SET, array_keys($server->data[$key])));
});

$server->setHandler('hSet', function ($fd, $data) use ($server) {
    if (count($data) < 3) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hSet' command"));
    }

    $key = $data[0];
    if (!isset($server->data[$key])) {
        $array[$key] = array();
    }
    $field = $data[1];
    $value = $data[2];
    $count = !isset($server->data[$key][$field]) ? 1 : 0;
    $server->data[$key][$field] = $value;
    return $server->send($fd, Server::format(Server::INT, $count));
});

$server->setHandler('hGetAll', function ($fd, $data) use ($server) {
    if (count($data) < 1) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'hGetAll' command"));
    }
    $key = $data[0];
    if (!isset($server->data[$key])) {
        return $server->send($fd, Server::format(Server::NIL));
    }
    return $server->send($fd, Server::format(Server::MAP, $server->data[$key]));
});

$server->setHandler('KEYS', function ($fd, $data) use ($server) {
    if (count($data) == 0) {
        return $server->send($fd, Server::format(Server::ERROR, "ERR wrong number of arguments for 'GET' command"));
    }

    if (!$server->data) {
        return $server->send($fd, Server::format(Server::NIL));
    }

    $key = $data[0];
    if ($key == "*") {
        return $server->send($fd, Server::format(Server::SET, array_keys($server->data)));
    } else {
        $dataKeys = array_keys($server->data);
        $key = str_replace(["*", "?"], [".*", ".?"], $key);
        $values = array_filter($dataKeys, function ($value) use ($key) {
            return preg_match('/' . $key . '/i', $value, $mchs);
        });
        if ($values) {
            return $server->send($fd, Server::format(Server::SET, $values));
        }
    }
    return $server->send($fd, Server::format(Server::NIL));
});

//通过监听一个 WorkerStart 事件，每隔一秒将数据写入到 db 文件的操作。
$server->on('WorkerStart', function ($server) {
    $server->tick(10000, function () use ($server) {
        file_put_contents(DB_FILE, serialize($server->data));
    });
});

$server->start();

/*
运行后,打开redis客户端直接可以连接

redis-cli -h 127.0.0.1 -p 9501
127.0.0.1:9501> set name haha
OK
127.0.0.1:9501> get name
"haha"
127.0.0.1:9501>


然后在同级目录下生成了一个db文件,存储的就是redis的数据,存储的事序列化后的数据:
a:1:{s:4:"name";s:4:"haha";}

*/